
#include "DCMProductIds.h"
#include "LicActivation.h"
#include "utilprod.h"
#include "utilcvt.h"
#include "time.h"

const unsigned short kRefTime = 0x02000; // 2002 + day of year 1

unsigned short GetCurTime()
{
	unsigned short ret;

	time_t curTime = time(NULL);

	struct tm *ptm = gmtime(&curTime);

	short year = ptm->tm_year + 1900;

	year -= 2000;

	if(year < 0)
		return -1;

	ret = year << 12 | ptm->tm_yday;

	return ret;

}

char ToUpper(char c)
{
	if ( (c >= 'a') && (c <= 'z') )
		c = 'A' + (c- 'a');
	return c;
}

char ToLower(char c)
{
	if ( (c >= 'A') && (c <= 'Z') )
		c = 'a' + (c- 'A');
	return c;
}

#define _CLIENT_SIDE

unsigned long Hash(const char* s, int len)
{
#ifdef _CLIENT_SIDE
#ifdef USE_DCMLICENSE
	CUTILprod2 prod(0, NULL);
#else
	CUTILprod prod(0, NULL);
#endif
	return prod.CheckSum2((char*)s, len);
#else
	CUTILprod::CheckSum2((char*)s, len);
#endif
}

int Scramble(unsigned long skey, char *sdata, void *data, unsigned long len)
{
#ifdef _CLIENT_SIDE
#ifdef USE_DCMLICENSE
	CUTILprod2 prod(0, NULL);
#else
	CUTILprod prod(0, NULL);
#endif
	return prod.Scramble2(skey, sdata, data, len);
#else
#ifdef USE_DCMLICENSE
	return CUTILprod2::Scramble2(skey, sdata, data, len);
#else
	return CUTILprod::Scramble2(skey, sdata, data, len);
#endif
#endif
}

int Scramble3(unsigned long skey, char *sdata, void *data, unsigned long len)
{
#ifdef _CLIENT_SIDE
#ifdef USE_DCMLICENSE
	CUTILprod2 prod(0, NULL);
#else
	CUTILprod prod(0, NULL);
#endif
	return prod.Scramble3(skey, sdata, data, len);
#else
#ifdef USE_DCMLICENSE
	return CUTILprod2::Scramble3(skey, sdata, data, len);
#else
	return CUTILprod::Scramble3(skey, sdata, data, len);
#endif
#endif
}

int Descramble(unsigned long skey, char *sdata, void *data, unsigned long len)
{
#ifdef _CLIENT_SIDE
#ifdef USE_DCMLICENSE
	CUTILprod2 prod(0, NULL);
#else
	CUTILprod prod(0, NULL);
#endif
	int ret = prod.Descramble2(skey, sdata, data, len);
	return ret;
#else
	return CUTILprod::Descramble2(skey, sdata, data, len);
#endif
}

int Descramble3(unsigned long skey, char *sdata, void *data, unsigned long len)
{
#ifdef _CLIENT_SIDE
#ifdef USE_DCMLICENSE
	CUTILprod2 prod(0, NULL);
#else
	CUTILprod prod(0, NULL);
#endif
	int ret = prod.Descramble3(skey, sdata, data, len);
	return ret;
#else
	return CUTILprod::Descramble3(skey, sdata, data, len);
#endif
}

// this structure can not have a vtable.
class CLicActInfo
{
public:
	unsigned char	m_ProductId;
	unsigned char	m_IdHash[kUidHashLen];
	unsigned char	m_OptionBits[kOptBitSize];

	CLicActInfo()
	{	
		m_ProductId = kProdIDNone;
		memset(m_IdHash, 0, kUidHashLen);
		memset(m_OptionBits, 0, kOptBitSize);
	}
	void SetOpts(unsigned long opts)
	{
		int i;
		memset(m_OptionBits, 0, kOptBitSize);
		for(i = 0; i < sizeof(unsigned long); i++)
			m_OptionBits[i] |= (opts >> (i*8));
	}
	void GetOpts(unsigned long &opts)
	{
		int i;
		for(i = 0; i < sizeof(unsigned long); i++)
			opts |= m_OptionBits[i] << (i*8);
	}

	void Copyto(LPVOID pBuf)
	{
		memcpy(pBuf, &m_ProductId, sizeof(m_ProductId));
		memcpy((char*) pBuf + sizeof(m_ProductId), &m_IdHash, sizeof(m_IdHash));
		memcpy((char *)pBuf + sizeof(m_IdHash) + sizeof(m_ProductId), &m_OptionBits, sizeof(m_OptionBits));
	}
	void Copyfrom(LPVOID pBuf)
	{
		memcpy(&m_ProductId, pBuf, sizeof(m_ProductId));
		memcpy(&m_IdHash, (char*) pBuf + sizeof(m_ProductId), sizeof(m_IdHash));
		memcpy(&m_OptionBits, (char *)pBuf + sizeof(m_IdHash) + sizeof(m_ProductId), sizeof(m_OptionBits));
	}
};

//const TCHAR *kSynchName = _T("__BBADCBF7-27F5-47CF-8B4A-8E0B8A8236C0__");
class CLicActInfo2
{
public:
	unsigned char		m_ProductId;
	unsigned char		m_IdHash[kUidHashLen];
	unsigned char		m_OptionBits[kOptBitSize2];

	CLicActInfo2()
	{	
		m_ProductId = kProdIDNone;
		memset(m_OptionBits, 0, kOptBitSize2);
		memset(m_IdHash, 0, kUidHashLen);
	}
	void SetOpts(unsigned long opts)
	{
		int i;
		memset(m_OptionBits, 0, kOptBitSize2);
		for(i = 0; i < sizeof(unsigned long); i++)
			m_OptionBits[i] |= (opts >> (i*8));
	}
	void GetOpts(unsigned long &opts)
	{
		int i;
		for(i = 0; i < sizeof(unsigned long); i++)
			opts |= m_OptionBits[i] << (i*8);
	}

	void SetOpts(unsigned __int64 opts)
	{
		int i;
		memset(m_OptionBits, 0, kOptBitSize2);
		for(i = 0; i < sizeof(unsigned __int64); i++)
			m_OptionBits[i] |= (opts >> (i*8));
	}
	void GetOpts(unsigned __int64 &opts)
	{
		int i;
		for(i = 0; i < sizeof(unsigned __int64); i++)
		{
			unsigned __int64 cur = m_OptionBits[i];
			opts |=  cur << (i*8);
		}
	}

	void Copyto(LPVOID pBuf)
	{
		memcpy(pBuf, &m_ProductId, sizeof(m_ProductId));
		memcpy((char*) pBuf + sizeof(m_ProductId), &m_IdHash, sizeof(m_IdHash));
		memcpy((char *)pBuf + sizeof(m_IdHash) + sizeof(m_ProductId), &m_OptionBits, sizeof(m_OptionBits));
	}
	void Copyfrom(LPVOID pBuf)
	{
		memcpy(&m_ProductId, pBuf, sizeof(m_ProductId));
		memcpy(&m_IdHash, (char*) pBuf + sizeof(m_ProductId), sizeof(m_IdHash));
		memcpy(&m_OptionBits, (char *)pBuf + sizeof(m_IdHash) + sizeof(m_ProductId), sizeof(m_OptionBits));
	}
};

class CLicActInfo3
{
public:
	unsigned char		m_ProductId;
	unsigned char		m_IdHash[kUidHashLen];
	unsigned char		m_OptionBits[kOptBitSize2];
	unsigned short		m_TimeCode;

	CLicActInfo3()
	{	
		m_ProductId = kProdIDNone;
		memset(m_OptionBits, 0, kOptBitSize2);
		memset(m_IdHash, 0, kUidHashLen);
		m_TimeCode = 0;
	}
	void SetOpts(unsigned long opts)
	{
		int i;
		memset(m_OptionBits, 0, kOptBitSize2);
		for(i = 0; i < sizeof(unsigned long); i++)
			m_OptionBits[i] |= (opts >> (i*8));
	}
	void GetOpts(unsigned long &opts)
	{
		int i;
		for(i = 0; i < sizeof(unsigned long); i++)
			opts |= m_OptionBits[i] << (i*8);
	}

	void SetOpts(unsigned __int64 opts)
	{
		int i;
		memset(m_OptionBits, 0, kOptBitSize2);
		for(i = 0; i < sizeof(unsigned __int64); i++)
			m_OptionBits[i] |= (opts >> (i*8));
	}
	void GetOpts(unsigned __int64 &opts)
	{
		int i;
		for(i = 0; i < sizeof(unsigned __int64); i++)
		{
			unsigned __int64 cur = m_OptionBits[i];
			opts |=  cur << (i*8);
		}
	}

	void Copyto(LPVOID pBuf)
	{
		memcpy(pBuf, &m_ProductId, sizeof(m_ProductId));
		memcpy((char*) pBuf + sizeof(m_ProductId), &m_IdHash, sizeof(m_IdHash));
		memcpy((char *)pBuf + sizeof(m_IdHash) + sizeof(m_ProductId), &m_OptionBits, sizeof(m_OptionBits));
		memcpy((char*) pBuf + sizeof(m_IdHash) + sizeof(m_ProductId) + sizeof(m_OptionBits), &m_TimeCode, sizeof(m_TimeCode));
	}
	void Copyfrom(LPVOID pBuf)
	{
		memcpy(&m_ProductId, pBuf, sizeof(m_ProductId));
		memcpy(&m_IdHash, (char*) pBuf + sizeof(m_ProductId), sizeof(m_IdHash));
		memcpy(&m_OptionBits, (char *)pBuf + sizeof(m_IdHash) + sizeof(m_ProductId), sizeof(m_OptionBits));
		memcpy(&m_TimeCode, (char*) pBuf + sizeof(m_IdHash) + sizeof(m_ProductId) + sizeof(m_OptionBits), sizeof(m_TimeCode));
	}
};

// class that will spread out the license bytes so that slight changes 
// produce bigger variations inthe overall key

class CDispersedLicBits
{
public:

	unsigned char m_buf[sizeof(CLicActInfo3)];

	CDispersedLicBits()
	{
		memset(m_buf, 0, sizeof(m_buf));
	};

	CDispersedLicBits(CLicActInfo3 inf)
	{
		int size = sizeof(CLicActInfo3);
		int i, j;

		int rem = size %4;

		unsigned char* p = (unsigned char*) &inf;
		for(i = 0, j=0; i < sizeof(CLicActInfo3)/4; i++, j+=4)
			m_buf[j] = *(p+i);

		for(j = 1; i < sizeof(CLicActInfo3)/2; i++, j+=4)
			m_buf[j] = *(p+i);

		for(j = 2; i < sizeof(CLicActInfo3)-sizeof(CLicActInfo3)/4; i++, j+=4)
			m_buf[j] = *(p+i);

		for(j = 3; i < sizeof(CLicActInfo3) - rem; i++, j+=4)
			m_buf[j] = *(p+i);

		// handle any remainder (size %4 != 0)
		for(j = i; i < sizeof(CLicActInfo3); i++, j++)
			m_buf[j] = *(p+i);
	}

	void Restore(CLicActInfo3 & inf)
	{
		int size = sizeof(CLicActInfo3);

		int rem = size %4;
		int i, j;


		unsigned char* p = (unsigned char*) &inf;
		for(i = 0, j=0; i < sizeof(CLicActInfo3)/4; i++, j+=4)
			*(p+i) = m_buf[j];

		for(j = 1; i < sizeof(CLicActInfo3)/2; i++, j+=4)
			*(p+i) = m_buf[j];

		for(j = 2; i < sizeof(CLicActInfo3)-sizeof(CLicActInfo3)/4; i++, j+=4)
			*(p+i) = m_buf[j];

		for(j = 3; i < sizeof(CLicActInfo3) - rem; i++, j+=4)
			*(p+i) = m_buf[j];

		// handle any remainder (size %4 != 0)
		for(j = i; i < sizeof(CLicActInfo3); i++, j++)
			*(p+i) = m_buf[j];
	}
};
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CLicActivation::CLicActivation()
{


}

CLicActivation::~CLicActivation()
{

}

int CLicActivation::GenerateLicense(unsigned short prodId,
									const char *email, 
									const char *pwd, 
									const char* ccInfo,
									unsigned long opts,
									char* psbuf)
{
	unsigned long cs, key;
	CLicActInfo inf;
	unsigned char buf1[sizeof(inf) + sizeof(unsigned long)];
	char buf2[kMinKeyBufSize];

	if(psbuf == NULL)
		return -1;

	memset(buf1, 0, sizeof(buf1));
	memset(buf2, 0, sizeof(buf1));

	inf.m_ProductId = (unsigned char)prodId;
	inf.SetOpts(opts);
	HashUserInfo(email, pwd, inf.m_IdHash);
	cs = Hash((char*) &inf, sizeof(inf));
	CUTILcvt::LittleEndian((unsigned long *)buf1, &cs);
	inf.Copyto(buf1 + sizeof(unsigned long));
	key = Hash((char*)ccInfo, strlen(ccInfo));

	if(key != 0)
	{
		Scramble(key, buf2, buf1, sizeof(buf1));
		SplitKey(buf2, psbuf);
	}

	return 0;
}

int CLicActivation::GenerateLicense2(unsigned short prodId,
									const char *email, 
									const char *pwd, 
									const char* ccInfo,
									unsigned __int64 opts,
									char* psbuf)
{
	unsigned long cs, key;
	CLicActInfo2 inf;
	unsigned char buf1[sizeof(inf) + sizeof(unsigned long)];
	char buf2[kMinKeyBufSize];

	if(psbuf == NULL)
		return -1;

	memset(buf1, 0, sizeof(buf1));
	memset(buf2, 0, sizeof(buf1));

	inf.m_ProductId = (unsigned char) prodId;
	inf.SetOpts(opts);
	HashUserInfo(email, pwd, inf.m_IdHash);
	cs = Hash((char*) &inf, sizeof(inf));
	CUTILcvt::LittleEndian((unsigned long *)buf1, &cs);
	inf.Copyto(buf1 + sizeof(unsigned long));
	key = Hash((char*)ccInfo, strlen(ccInfo));

	if(key != 0)
	{
		Scramble(key, buf2, buf1, sizeof(buf1));
		SplitKey(buf2, psbuf);
	}

	return 0;
}

int CLicActivation::GenerateLicense3(unsigned short prodId,
									const char *email, 
									const char *pwd, 
									const char* ccInfo,
									unsigned __int64 opts,
									char* psbuf)
{
	unsigned long cs, key;
	CLicActInfo3 inf;
	unsigned char buf1[sizeof(inf) + sizeof(unsigned long)];
	char buf2[kMinKeyBufSize];

	if(psbuf == NULL)
		return -1;

	memset(buf1, 0, sizeof(buf1));
	memset(buf2, 0, sizeof(buf1));

	inf.m_ProductId = (unsigned char) prodId;
	inf.SetOpts(opts);

	unsigned short tc = GetCurTime();
	CUTILcvt::LittleEndian(&inf.m_TimeCode, &tc);
	HashUserInfo(email, pwd, inf.m_IdHash);
	cs = Hash((char*) &inf, sizeof(inf));
	CUTILcvt::LittleEndian((unsigned long *)buf1, &cs);
	CDispersedLicBits sb(inf);
//	inf.Copyto(buf1 + sizeof(unsigned long));
	memcpy(buf1 + sizeof(unsigned long), sb.m_buf, sizeof(sb.m_buf));
	key = Hash((char*)ccInfo, strlen(ccInfo));

	if(key != 0)
	{
		Scramble3(key, buf2, buf1, sizeof(buf1));
		SplitKey(buf2, psbuf);
	}

	return 0;
}


void UnpackBuf(unsigned char *buf, CLicActInfo &inf, unsigned long &cs, unsigned long &cs2)
{
	unsigned long tmp;

	CUTILcvt::LittleEndian(&cs, buf);
	inf.Copyfrom(buf + sizeof(cs));
	tmp = Hash((char*)&inf, sizeof(inf));
	CUTILcvt::LittleEndian(&cs2, &tmp);
}

void UnpackBuf(unsigned char *buf, CLicActInfo2 &inf, unsigned long &cs, unsigned long &cs2)
{
	unsigned long tmp;

	CUTILcvt::LittleEndian(&cs, buf);
	inf.Copyfrom(buf + sizeof(cs));
	tmp = Hash((char*)&inf, sizeof(inf));
	CUTILcvt::LittleEndian(&cs2, &tmp);
}

void UnpackBufDisp(unsigned char *buf, CLicActInfo3 &inf, unsigned long &cs, unsigned long &cs2)
{
	unsigned long tmp;

	CDispersedLicBits sb;

	CUTILcvt::LittleEndian(&cs, buf);
	memcpy(sb.m_buf, buf + sizeof(cs), sizeof(sb.m_buf)); 

	sb.Restore(inf);
	tmp = Hash((char*) &inf, sizeof(inf));
	CUTILcvt::LittleEndian(&cs2, &tmp);
}

int CLicActivation::GetLicenseInfo(const char *email, 
								   const char *pwd, 
								   const char *lic, 
								   const char* ccInfo, 
								   unsigned short *pProdId,
								   unsigned long *pOpts)
{
	unsigned char	idHash[kUidHashLen];
	unsigned long cs, cs2, key;
	CLicActInfo inf;
	unsigned char buf[sizeof(inf) + sizeof(cs)];
	char licbuf[kMinKeyBufSize];
	*pOpts = 0;
	*pProdId = 0;

	HashUserInfo(email, pwd, idHash);

	key = Hash((char*)ccInfo, strlen(ccInfo));
	if(key != 0)
	{
		MergeKey(lic, licbuf);
		if(Descramble(key, licbuf, buf, strlen(licbuf)) == 0)
		{
			UnpackBuf(buf, inf, cs, cs2);
			if(cs2 != cs)
			{
				char xlbuf[kMinKeyBufSize];
				MergeKey(lic, licbuf);
				CUTILprod::TranslateOldKey(licbuf, xlbuf);
				if(Descramble(key, xlbuf, buf, strlen(licbuf)) == 0)
					UnpackBuf(buf, inf, cs, cs2);
			}

			DWORD *pdw = (DWORD*) &inf.m_OptionBits;

 			if(cs2 == cs)
			{
				if(memcmp(&idHash, &(inf.m_IdHash), kUidHashLen) == 0)
				{
					inf.GetOpts(*pOpts);
					*pProdId = inf.m_ProductId;
					return 0;
				}
			}
		}
	}
	return -1;
}

int CLicActivation::GetLicenseInfo2(const char *email, 
								   const char *pwd, 
								   const char *lic, 
								   const char* ccInfo, 
								   unsigned short *pProdId,
								   unsigned __int64 *pOpts)
{
	unsigned char	idHash[kUidHashLen];
	unsigned long cs, cs2, key;
	CLicActInfo2 inf;
	unsigned char buf[sizeof(inf) + sizeof(cs)];
	char licbuf[kMinKeyBufSize];
	*pOpts = 0;
	*pProdId = 0;

	HashUserInfo(email, pwd, idHash);

	key = Hash((char*)ccInfo, strlen(ccInfo));
	if(key != 0)
	{
		MergeKey(lic, licbuf);
		if(Descramble(key, licbuf, buf, strlen(licbuf)) == 0)
		{
			UnpackBuf(buf, inf, cs, cs2);
			if(cs2 != cs)
			{
				char xlbuf[kMinKeyBufSize];
				MergeKey(lic, licbuf);
				CUTILprod::TranslateOldKey(licbuf, xlbuf);
				if(Descramble(key, xlbuf, buf, strlen(licbuf)) == 0)
					UnpackBuf(buf, inf, cs, cs2);
			}

 			if(cs2 == cs)
			{
				if(memcmp(&idHash, &(inf.m_IdHash), kUidHashLen) == 0)
				{
					inf.GetOpts(*pOpts);
					*pProdId = inf.m_ProductId;
					return 0;
				}
			}
		}
	}
	return -1;
}

int CLicActivation::GetLicenseInfo3(const char *email, 
								   const char *pwd, 
								   const char *lic, 
								   const char* ccInfo, 
								   unsigned short *pProdId,
								   unsigned __int64 *pOpts)
{
	unsigned char	idHash[kUidHashLen];
	unsigned long cs, cs2, key;
	CLicActInfo3 inf;
	unsigned char buf[sizeof(inf) + sizeof(cs)];
	char licbuf[kMinKeyBufSize];
	*pOpts = 0;
	*pProdId = 0;

	HashUserInfo(email, pwd, idHash);

	key = Hash((char*)ccInfo, strlen(ccInfo));
	if(key != 0)
	{
		MergeKey(lic, licbuf);
		if(Descramble3(key, licbuf, buf, strlen(licbuf)) == 0)
		{
			UnpackBufDisp(buf, inf, cs, cs2);

 			if(cs2 == cs)
			{
				if(memcmp(&idHash, &(inf.m_IdHash), kUidHashLen) == 0)
				{
					if(GetCurTime() - inf.m_TimeCode > 10)
						return -1;

					inf.GetOpts(*pOpts);
					*pProdId = inf.m_ProductId;
					return 0;
				}
			}
		}
	}
	return -1;
}


void CLicActivation::HashUserInfo(const char *uid, const char *pwd, unsigned char buf[kUidHashLen])
{
	unsigned int i;
	char *uidbuf = new char[strlen(uid)+1];
	char *pwdbuf = new char[strlen(pwd)+1];

	for(i = 0; i <= strlen(uid); i++)
		uidbuf[i] = ToLower(uid[i]);

	for(i = 0; i <= strlen(pwd); i++)
		pwdbuf[i] = ToLower(pwd[i]);

	unsigned long huid = Hash(uidbuf, strlen(uidbuf));
	unsigned long hpwd = Hash(pwdbuf, strlen(pwdbuf));
	unsigned long ret = huid^hpwd;
	buf[0] = (unsigned char) (huid >> 24);
	CUTILcvt::LittleEndian((unsigned long *) &buf[1], &ret);

	delete [] uidbuf;
	delete [] pwdbuf;
}


void CLicActivation::SplitKey(const char *key, char*sbuf)
{
	int idx = 0;

	unsigned int div = strlen(key)/4;

	while(*key)
	{
		*(sbuf++) = *(key++);
		if(*key && ++idx%div == 0 && strlen(key) >= div)
			*(sbuf++) = '-';
	}		
	*sbuf = '\0';

}

void CLicActivation::MergeKey(const char *sbuf, char*key)
{
	while(*sbuf)
	{
		if(*sbuf == '-') sbuf++;
		if((*(key++) = ToUpper(*sbuf)) != '\0') 
			sbuf++; // don't increment bast the null terminator
	}
	*key = '\0';
}
